#include "SypEngine.h"
#include "GE_SceneManager.h"

#ifdef CHECKMEM
#include "mmgr.h"
#endif
namespace Syp
{


SceneManager* SceneManager::sm = 0;

SceneManager::SceneManager()
{
	rnd = &Renderer::getRenderer();
	clearingScene = false;
	setNumLightsPerObj(1);
	activeCamera = 0;
	//Renderer::getRenderer().setGlobalAmbience(Point4(0.2f,0.2f,0.2f,1.0f));
}

SceneManager::~SceneManager()
{
	clearScene();	
}

void SceneManager::clearScene()
{
	clearingScene = true;
	//clears all MeshInstances
	std::vector<MeshInstance*>::iterator i = Instances.begin();
		
	while(i != Instances.end())
	{					
		MeshInstance::destroyMeshInstance((*i));
		(*i) = 0;
		++i;	
	}	

	//Clear Instances, SceneManager holds no responsibility over any MeshInstances now.
	Instances.clear();
	//Clear RenderQue
	renderables.clear();
	//Lets not get careless
	clearingScene = false;

}

void SceneManager::releaseSceneManager()
{
	if(sm)
	{
		delete sm;
		sm = 0;
	}
}


void SceneManager::initSceneManager()
{
	if(!sm)
	{
		sm = new SceneManager();		
	}
}

SceneManager& SceneManager::getSceneManager()
{
	assert(sm && "SceneManager cannot be returned until it has been initialised");
	return (*sm);
}

void SceneManager::beginDrawing()
{
	
	rnd->clearColorDepthBuffer();
	rnd->updatePolyDrawn();

	assert(activeCamera && "Active Camera has not been set or defined");
	if(activeCamera->isAnimated())
	{
		activeCamera->update();
	}
	rnd->setUpPerspective();

	for(PInt j=0;j<Lights.size();++j)
	{
	rnd->setUpLight(Lights[j]);
	}
	


	std::set<Renderable>::iterator i = renderables.begin();
	std::set<Renderable>::iterator end = renderables.end();

	//Set to correct mode
	while (i != end)
	{		
		rnd->draw(i->parent, i->indexBlock,i->pass);	
		++i;
	}

	///TODO: Add glFlush() here.
    
}

void SceneManager::endDrawing()
{
	rnd->swapBuffers();
}

void SceneManager::setActiveCamera(Camera* cam)
{
	assert(cam && "User attempted to pass in NULL value");
	activeCamera = cam;
	rnd->setActiveCamera(cam);
}

Camera* SceneManager::getActiveCamera()
{
	return activeCamera;
}

void SceneManager::setNumLightsPerObj(PByte numLights)
{
	lightsPerObj = numLights;
	Renderer::getRenderer().setUpLights(numLights);
}

void SceneManager::addInstance(MeshInstance *mi)
{
	//Insertion sorting
	//Order of sorting in terms of importance (Can change in future)
	// i) Need to sort by blending type first
	// ii)then each blend type by texture			**** ii / iii need experimenting
	// iii) then sort similar texture  instances from front to back (Ortho first)
	//Currently
	//1) Perspective/Ortho
	//2) Texture
	//3) Front - to -back
	if(!(mi->getMaterial() && mi->getMesh()) )
	{
		//Requires a material and mesh attached before it can be added.
		return;
	}
	PInt numDCs = mi->getMesh()->getNumDrawCalls();
	assert("No draw calls found in mesh. Proceeding with such behaviour will cause undefined behaviour" && numDCs);
	Renderable r;
	//Split by number of drawcalls/parts of mesh
	for(PInt i = 0;i<numDCs;++i)
	{
		r.parent = mi;
		r.indexBlock = i;
		for(PInt j = 0;j < mi->getMaterial()->subMaterials[ mi->getMesh()->getDrawCall(i).materialID ].getEffect()->getNumPasses();++j )
		{//for every pass create a renderable			
			r.pass = mi->getMaterial()->subMaterials[ mi->getMesh()->getDrawCall(i).materialID ].getEffect()->getPass(j);
				// Pair ( setIterator, bool )
		std::pair<	std::set<Renderable>::iterator ,bool > result = renderables.insert(r);
		assert(result.second && "Something is wrong with your theory, a similar element was encountered and inserted into Renderables Que. Or the user is doing something stupid. ie Adding the same MeshInstance twice");
		//Recording purposes
		mi->renderables.push_back(result.first);

		}
		
		
		////TODO: Do insertion sort here
	}

		Instances.push_back(mi);	
}


void SceneManager::addLight(Light* l)
{
	Lights.push_back(l);
}

void SceneManager::removeLight(Light* l)
{
    std::vector<Light*>::iterator i = std::find(Lights.begin(), Lights.end(), l);
	//Get rid of it from the que
	if(i != Lights.end())
	{
		Lights.erase(i);
	}
}

void SceneManager::removeInstance(MeshInstance* mi)
{
	if(clearingScene)
	{
		//We do not proceed to remove it from the Scene because the entire scene will be cleared anyway and we do not want to have complications bcos of iterators being invalidated.
		return;
	}
	//Find the instance
	std::vector<MeshInstance*>::iterator i = std::find(Instances.begin(), Instances.end(), mi);
	//Get rid of it from the que
	if(i != Instances.end())
	{
		Instances.erase(i);
	}
	else
	{// The MeshInstance was not found at all, it is probably removed by user earlier and is being deleted now thus causing the safety feature in MeshInstance() destructor to help in removing it.This is a good thing(tm)
		return;

	}
		

	/////////////Renderables/////////////
	//Vector of Set(RenderableIterators)
	//beg and end are iterators of the vector which stores renderable iterators.
	//This removes all Renderables from the renderque
	std::vector< std::set<Renderable>::iterator >::iterator beg = mi->renderables.begin();
	std::vector< std::set<Renderable>::iterator >::iterator end = mi->renderables.end();

	while(beg != end)
	{
		renderables.erase( (*beg) );
		++beg;
	}
	//clears all records of renderables in MeshInstance
	mi->renderables.clear();

}


void SceneManager::dumpRenderQue()
{
	std::set<Renderable>::iterator i = renderables.begin();
	ErrorLogger::getLogger()<<"-----------------------RenderQue Dump-----------------------------"<<std::endl;
	ErrorLogger::getLogger()<<"MeshInstances in Memory : "<<static_cast<PInt>(Instances.size())<<std::endl;
	ErrorLogger::getLogger()<<"Renderables in Memory : "<<static_cast<PInt>(renderables.size())<<std::endl;
	while(i != renderables.end())
	{
		ErrorLogger::getLogger()<<"********************"<<std::endl;
		ErrorLogger::getLogger()<<"Mesh "<<std::endl;
		i->parent->getMesh()->dumpResourceInfo();
		ErrorLogger::getLogger()<<"Material "<<std::endl;
		i->parent->getMaterial()->dumpResourceInfo();
		ErrorLogger::getLogger()<<"DrawCallIndex "<<static_cast<PInt>(i->indexBlock)<<std::endl;
		ErrorLogger::getLogger()<<"Pass "<<i->pass->getName()<<" Shader\n";
		if(i->pass->getShaderProgram())
		{
		i->pass->getShaderProgram()->dumpResourceInfo();
		}
		ErrorLogger::getLogger()<<"********************"<<std::endl;
		++i;
        
	}
	ErrorLogger::getLogger()<<"-----------------------RenderQue Dump End--------------------------"<<std::endl;
}
}
